<?php
namespace app\api\controller;

use app\common\controller\Api;
use app\api\validate\Trades as TradesValidate;
use app\common\model\ShareStrategy;
use app\common\model\StrategyTransaction;
use app\common\model\Stock;
use app\common\model\Config;
use think\Db;

/**
 * 交易控制器 
 * 方法列表：创建策略、补充、平仓
 */
class Trade extends Api
{
    // 无需登录的接口,*表示全部
    protected $noNeedLogin = [];
    // 无需鉴权的接口,*表示全部
    protected $noNeedRight = ['*'];

    /**
     * 控制器初始化工作
     * 限制：
     *   -在交易时间段内方能交易
     *   -用户实名认证后方能交易
     */
    public function _initialize()
    {
        parent::_initialize();

        $userInfo = $this->auth->getUser();
        
        if( empty($userInfo->real_name) ){
            $this->error('请先设置实名信息再操作！');
        }

        if( !isTradeTime() ){
            $this->error('当前时间不在交易时间段内');
        }
    }

     /**
      * 创建策略
      * @method POST
      * @param allNo 股票编码
      * @param price 股价
      * @param num   数量
      * @param isAuto 是否自动管理
      */
     public function createStrategy()
     {
        $params = $this->request->post();
        $validate = new TradesValidate;
        if(!$validate->scene('createStrategy')->check($params)){
            return $this->error($validate->getError());
        }
        
        $userInfo = $this->auth->getUser();
        $strategyInfo = ShareStrategy::where([
            'user_id' => $userInfo->id,
            'all_no'  => $params['allNo'],
            'status'  => 1 
            ])->find();
        if( $strategyInfo ){
            $this->error('已有创建策略');
        }

        //获取股票信息
        $stockname = Stock::where('all_no',$params['allNo'])->value('name');
        if(isset($params['id'])) unset($params['id']);
        //获取金额信息
        $conList = Config::getVal(['min_buy_num','management_expense','add_warehouse']);
        //保证金
        $earnestMoney = $params['price'] * $params['num'] / 10;
        //管理费
        if( $params['isAuto'] == 1 ){
            $managementExpense = $earnestMoney * $conList['management_expense'] * 0.01 * 10;
        }else{
            $managementExpense = 0;
        }
        //建仓费
        $addWarehouse = $earnestMoney * 10 * $conList['add_warehouse'];
        
        if( ($earnestMoney + $managementExpense + $addWarehouse) > $userInfo->money ){
            $this->error('余额不足');
        }

        Db::startTrans();
        try{
            //组装数据
            $params['user_id']          = $userInfo->id;
            $params['name']             = $stockname;
            $params['all_no']           = $params['allNo'];
            $params['total_lever_num']  = $params['num'];
            $params['is_auto']          = $params['isAuto'];
            $params['total_earnest']    = $params['price'] * $params['num'] / 10;

            //创建新策略
            $strategyInfo = new ShareStrategy();
            $strategyInfo->allowField(true)->save($params);

            //新增交易记录
            $strategyTransaction                = new StrategyTransaction();
            $strategyTransaction->user_id       = $userInfo->id;
            $strategyTransaction->strategy_id   = $strategyInfo->id;
            $strategyTransaction->buy_price     = $params['price'];
            $strategyTransaction->lever_num     = $params['num'];
            $strategyTransaction->type          = 'new';
            $strategyTransaction->name          = $stockname;
            $strategyTransaction->no            = $params['allNo'];
            $strategyTransaction->buy_time      = time();
            $strategyTransaction->earnest       = $params['price'] * $params['num'] / 10;
            $strategyTransaction->save();

            //扣除余额
            updateMoney($userInfo,-$earnestMoney,'保证金');
            //建仓费
            if( $addWarehouse > 0 ){
                updateMoney($userInfo,-$addWarehouse,'建仓费');
            }
            if( $managementExpense > 0 ){
                updateMoney($userInfo,-$managementExpense,'管理费');
            }

            Db::commit();
            $this->clearStrategyInfoCache($userInfo->id);
        }catch (\Exception $e){
            Db::rollback();
            $this->error('生成策略失败：'.$e->getMessage());
        }
        $this->success('生成策略成功');
    }

      /**
       * 补仓追加
       * @method POST
       * @param id 策略id
       * @param price 现价
       * @param num 补充数量
       */
      public function supplement()
      {
        $params = $this->request->post();
        $validate = new TradesValidate;
        if(!$validate->scene('addNum')->check($params)){
            return $this->error($validate->getError());
        }
        $userInfo = $this->auth->getUser();
        $strategyInfo = ShareStrategy::where([
            'id'      => $params['id'],
            'user_id' => $userInfo->id,
            'status'  => 1
            ])->find();
        if( !$strategyInfo ){
            $this->error('没有找到策略信息');
        }

        //获取股票信息
        $stockInfo = getRealTimeStockInfo($strategyInfo->all_no)[$strategyInfo->all_no];

        //获取金额信息
        $conList = Config::getVal(['min_buy_num','management_expense','add_warehouse']);
        //保证金
        $earnestMoney = $stockInfo['price'] * $params['num'] / 10;
        //管理费
        $managementExpense = $strategyInfo['is_auto'] == 1 ? $earnestMoney * $conList['management_expense'] * 0.01 * 10 : 0;
        //建仓费
        $addWarehouse = $earnestMoney * 10 * $conList['add_warehouse'];
        if( ($earnestMoney + $managementExpense + $addWarehouse) > $userInfo->money ){
            throw new \Exception('余额不足');
        }
        
        Db::startTrans();
        try{
            //修改策略数据
            $strategyInfo->total_lever_num += $params['num'];
            $strategyInfo->total_earnest += $params['num'] * $stockInfo['price'] / 10;
            $strategyInfo->updatetime = time();
            $strategyInfo->save();

            //创建策略交易记录
            $strategyTransaction                = new StrategyTransaction();
            $strategyTransaction->user_id       = $userInfo->id;
            $strategyTransaction->strategy_id   = $strategyInfo->id;
            $strategyTransaction->buy_price     = $stockInfo['price'];
            $strategyTransaction->lever_num     = $params['num'];
            $strategyTransaction->name          = $strategyInfo['name'];
            $strategyTransaction->no            = $strategyInfo['all_no'];
            $strategyTransaction->buy_time      = time();
            $strategyTransaction->earnest       = $stockInfo['price'] * $params['num'] / 10;
            $strategyTransaction->save();

            //扣除余额
            updateMoney($userInfo,$earnestMoney * -1,'保证金');
            if( $addWarehouse > 0 ){
                updateMoney($userInfo,$addWarehouse * -1,'建仓费');
                //交易佣金
                updateMoney($userInfo,$stockInfo['price'] * $params['num'] * $conList['commission'] * 0.01 *-1,'交易佣金');
            }
            if( $managementExpense > 0 ){
                updateMoney($userInfo,$managementExpense * -1,'管理费');
            }

            Db::commit();
            // 清除原来用户的持仓缓存
            $this->clearStrategyInfoCache($userInfo->id);
        }catch (\Exception $e){
            Db::rollback();
            $this->error('补充失败：'.$e->getMessage());
        }
        $this->success('补充成功');
    }

    /**
     * 平仓
     * @method POST
     * @param id 策略id
     * @param num 平仓数量
     */
    public function pay()
    {
        $params = $this->request->post();
        $validate = new TradesValidate;
        if(!$validate->scene('pay')->check($params)){
            return $this->error($validate->getError());
        }
        
        $userInfo = $this->auth->getUser();
        $strategyInfo = ShareStrategy::where([
            'user_id' => $userInfo->id,
            'id'      => $params['id'],
            'status'  => 1
            ])->find();
        
        if( !$strategyInfo ){
            $this->error('没有找到策略信息');
        }
        
        if($strategyInfo['can_use_num'] < $params['num']){
            $this->error('当前可交易数量不足');
        }
        
        //获取金额信息
        $conList = Config::getVal(['commission','stamp_duty','transfer_fee']);

        //获取股票信息
        $stockInfo = getRealTimeStockInfo($strategyInfo->all_no)[$strategyInfo->all_no];

        Db::startTrans();
        try {
            $strategy = [];
            //获取平仓剩余
            $residue = bcsub($strategyInfo->total_lever_num,$params['num']);
            if(  $residue <= 0){
                $strategy['status'] = 0;
            }
            //策略表里总保证金要扣除的保证金
            $earnest = intval($strategyInfo->total_earnest)/intval($strategyInfo->total_lever_num)*intval($params['num']);

            $strategy['id'] = $strategyInfo->id;
            $strategy['total_lever_num'] = $residue;
            $strategy['total_earnest'] = bcsub($strategyInfo->total_earnest,$earnest);
            $strategy['can_use_num'] = bcsub($strategyInfo->can_use_num,$params['num']);

            ShareStrategy::update($strategy);
            
            //持有股票的保证金平仓后要退回到可用资金
            updateMoney($userInfo,$earnest,'保证金');
            
            $strategyInfo->save();

            //创建策略交易记录
            $strategyTransaction = new StrategyTransaction();
            $transaction = [];
            $transaction['user_id']     = $userInfo->id;
            $transaction['strategy_id'] = $strategyInfo->id;
            $transaction['type']        = 'reduce';
            $transaction['buy_price']   = bcdiv($strategyInfo->total_earnest,$strategyInfo->total_lever_num,2)*10;
            $transaction['pay_price']   = $stockInfo['price'];
            $transaction['lever_num']   = $params['num'];
            $transaction['name']        = $strategyInfo->name;
            $transaction['no']          = $strategyInfo->all_no;
            //保证金记录
            $transaction['earnest']     = $earnest;
            $transaction['pay_time']    = time();
            $transaction['buy_time']    = $strategyInfo->createtime;
            $transaction['createtime']  = time();
            $transaction['updatetime']  = time();
            $strategyTransaction->insert($transaction);
            //每股盈亏
            $average_price = bcdiv($strategyInfo->total_earnest,$strategyInfo->total_lever_num);
            $profit = bcsub($stockInfo['price'],$average_price) * 10;
            //平仓后的钱
            updateMoney($userInfo,$profit * intval($params['num']),'盈亏');
            //交易佣金
            updateMoney($userInfo,floatval($stockInfo['price']) * intval($params['num']) * floatval($conList['commission']) * -1,'交易佣金');
            //印花税
            updateMoney($userInfo,floatval($stockInfo['price']) * intval($params['num']) * floatval($conList['stamp_duty']) * -1,'印花税');
            //过户费
            updateMoney($userInfo,floatval($stockInfo['price']) * intval($params['num']) * floatval($conList['transfer_fee']) * -1,'过户费');
            //记录累计盈亏
            updateCumulative($userInfo,$profit * intval($params['num']),'累计盈亏');
            
            Db::commit();
            $this->clearStrategyInfoCache($userInfo->id);
        }catch (\Exception $e){
            Db::rollback();
            $this->error('平仓失败',$e->getMessage());
        }
        $this->success('平仓成功');
    }

    /**
     * 获取某只股票的交易(买入前)信息
     */
    public function getStockByTradeInfo()
    {
        $userInfo = $this->auth->getUser();
        $allNo = $this->request->param('all_no');
        if( !$allNo ){
            $this->error('股票代码不能为空');
        }
        list($list,$stock) = $this->getStockInfoByBuySellTrade($allNo);
        $data = [
            'stock' => [
                'name'  => $stock[1],
                'code'  => $stock[2],
                'price' => $stock[3],
                'zf'    => $stock[32],
                'limit_up_price'   => $stock[47],
                'limit_down_price' => $stock[48]
            ],
            'list' => $list,
            'user_balance'   => $userInfo->money,
            'full_warehouse' => getHundredMultiple(bcdiv($userInfo->money,$stock[3]))
        ];
        $this->success('获取成功',$data);
    }

    /**
     * 获取某只股票的交易(卖出前)信息
     */
    public function getStockInfoBySell()
    {
        $userInfo = $this->auth->getUser();
        $allNo = $this->request->param('all_no');
        if( !$allNo ){
            $this->error('股票代码不能为空');
        }
        list($list,$stock) = $this->getStockInfoByBuySellTrade($allNo);
        $data = [
            'stock' => [
                'name'  => $stock[1],
                'code'  => $stock[2],
                'price' => $stock[3],
                'zf'    => $stock[32],
                'limit_up_price'   => $stock[47],
                'limit_down_price' => $stock[48]
            ],
            'list'        => $list
        ];
        $where = [
            'all_no'  => $allNo,
            'user_id' => $userInfo->id,
            'status'  => '1'
        ];
        $info = ShareStrategy::where($where)->find();
        if( !$info ){
            $this->error('不合法的卖出交易');
        }
        $data['total_lever_num'] = $info->total_lever_num;
        $data['can_use_num']     = $info->can_use_num;

        $this->success('获取成功',$data);
    }

    /**
     * 获取股票信息包括卖出买入相关
     */
    protected function getStockInfoByBuySellTrade($allNo)
    {
        $url = 'http://qt.gtimg.cn/q=' . $allNo;
        $res = file_get_contents($url);
        //转换字符编码
        $res = iconv('GBK', 'UTF-8', $res);
        $resData = array_filter(explode("\n", $res));
        $arrStockInfo = explode('~',$resData[0]);

        $list = [];
        $buy_start = 9;
        $length  = 5;
        $buy_end = $buy_start + $length*2;
        for ($i=$buy_start; $i < $buy_end; $i++) { 
            if( $i%2 == 0 ){
                $list['buy'][] = [
                    $arrStockInfo[$i-1],
                    $arrStockInfo[$i]
                ];
            }
        }
        
        $sell_start = $buy_end;
        $sell_end   = $sell_start + $length*2;
        for ($i=$sell_start; $i < $sell_end; $i++) { 
            if( $i%2 == 0 ){ 
                $list['sell'][] = [
                    $arrStockInfo[$i-1],
                    $arrStockInfo[$i]
                ];
            }
        }

        return [$list,$arrStockInfo];
    }

    /**
     * 清除用户持仓缓存
     */
    protected function clearStrategyInfoCache($uid)
    {
        // 清除原来用户的持仓缓存
        $redis = \think\Cache::store('redis')->handler();
        $redisKey = "stock_share_strategy:uid_$uid";
        if($redis->exists($redisKey)){
            $redis->del($redisKey);
        }
    }
    
}
